/**
 * Fichier JpaGeneriqueDao.java : Définition de classe
 * fr.unice.gnpt.core.dao.jpa.JpaDaoGenerique : Fabrique de DAO de type
 * Generique pour JPA
 * 
 * Auteurs : Groupe GNPT : Gastinel, Nicolas, Pellegrino, Trovato
 */
package fr.unice.gnpt.core.dao.jpa;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Properties;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import fr.unice.gnpt.core.dao.DaoException;
import fr.unice.gnpt.core.dao.DaoGenerique;
import fr.unice.gnpt.core.dao.connexion.Connexion;
import fr.unice.gnpt.core.dao.connexion.ConnexionJpa;

/**
 * Classe de gestion de DAO de type générique pour JPA.
 * 
 * @author gnpt
 * @version 0.0.3
 * 
 * @param <T>
 *            Le type d'objet associé au DAO.
 * @param <ID>
 *            Le type de l'identifiant unique associé à l'objet de type
 *            <code>T</code>.
 */
public abstract class JpaDaoGenerique<T, ID extends Serializable> implements
		DaoGenerique<T, ID> {
	/**
	 * Type de classe sur laquelle le DAO Générique travaille.
	 */
	private Class<? extends T> classeEntite;
	/**
	 * Joue le rôle de la connexion à la base de données.
	 */
	private Connexion<EntityManager> connexion;

	/**
	 * Constructeur par défaut.
	 * 
	 * @param classEntite
	 *            classe sur laquelle le DAO travaille.
	 * @param proprietesConnexion
	 *            l'objet Properties contenant les propriétés permettant
	 *            d'initialiser la connexion à la base de données.
	 */
	public JpaDaoGenerique(Class<? extends T> classEntite,
			Properties proprietesConnexion) {
		this.classeEntite = classEntite;
		this.connexion = new ConnexionJpa(proprietesConnexion);
	}

	/**
	 * @see fr.unice.gnpt.core.dao.DaoGenerique#create(java.lang.Object)
	 */
	@Override
	public void create(T objet) throws DaoException {
		try {
			this.connexion.getConnexion().persist(objet);
		} catch (Exception e) {
			throw new DaoException("Impossible de rendre persistant l'objet : "
					+ objet + ".", e);
		}
	}

	/**
	 * @see fr.unice.gnpt.core.dao.DaoGenerique#delete(java.lang.Object)
	 */
	@Override
	public void delete(T objet) throws DaoException {
		try {
			this.connexion.getConnexion().remove(objet);
		} catch (Exception e) {
			throw new DaoException(
					"Impossible de supprimer l'objet : " + objet, e);
		}

	}

	/**
	 * @see fr.unice.gnpt.core.dao.DaoGenerique#update(java.lang.Object)
	 */
	@Override
	public T update(T entite) throws DaoException {
		try {
			return this.connexion.getConnexion().merge(entite);
		} catch (Exception e) {
			throw new DaoException("Impossible de mettre à jour l'entite : "
					+ entite + ".", e);
		}
	}

	/**
	 * @see fr.unice.gnpt.core.dao.DaoGenerique#findAll()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Collection<T> findAll() throws DaoException {
		Query q;
		try {
			q = this.connexion.getConnexion()
					.createQuery(
							"SELECT c FROM "
									+ this.classeEntite.getSimpleName() + " c");
		} catch (Exception e) {
			throw new DaoException(
					"Impossible de récupérer tout les objets associés au type de DAO "
							+ this.classeEntite.getSimpleName() + ".", e);
		}
		return (new ArrayList<T>(q.getResultList()));
	}

	/**
	 * Accesseur au gestionnaire d'entité.
	 * 
	 * @return le gestionnaire d'entité.
	 */
	public Connexion<EntityManager> getConnexion() {
		return this.connexion;
	}

	/**
	 * Mutateur du gestionnaire d'entité.
	 * 
	 * @param connexion
	 *            le nouveau gestionnaire d'entité.
	 */
	@SuppressWarnings("unchecked")
	public void setConnexion(Connexion connexion) {
		this.connexion = connexion;
	}
}
